﻿namespace Gunningine
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    public class Root
    {
        private string resourceLocation;

        private List<IResourceLoader> resourceLoaders;

        private Type renderSystemType;

        private string windowTitle;

        private int windowWidth;

        private int windowHeight;

        private bool fullScreen;

        private bool useVSync;

        /// <summary>
        /// Constructor.
        /// </summary>
        public Root(string resourceLocation, 
                    List<IResourceLoader> resourceLoaders, 
                    Type renderSystemType,
                    string windowTitle,
                    int windowWidth,
                    int windowHeight,
                    bool fullScreen,
                    bool useVSync)
        {
            this.resourceLocation = resourceLocation;
            this.resourceLoaders = resourceLoaders;
            this.renderSystemType = renderSystemType;
            this.windowTitle = windowTitle;
            this.windowWidth = windowWidth;
            this.windowHeight = windowHeight;
            this.fullScreen = fullScreen;
            this.useVSync = useVSync;
        }

        /// <summary>
        /// Run the game.
        /// </summary>
        /// <param name="updatesPerSecond"></param>
        /// <param name="framesPerSecond"></param>
        public void Run(double updatesPerSecond, double framesPerSecond)
        {
            using (ResourceManager resourceMgr = new ResourceManager(this.resourceLocation))
            {
                // Set resource loaders
                foreach (var resourceLoader in this.resourceLoaders)
                {
                    resourceMgr.AddResourceLoader(resourceLoader);
                }

                using (ScriptManager scriptMgr = new ScriptManager())
                {
                    // Bootstrap the scripting system
                    resourceMgr.LoadBootstrap(scriptMgr);

                    // Now get all the games we've found in script.
                    List<Type> gameTypes = scriptMgr.GetAssignableScriptTypes(typeof(Gunningine.IGame));
                    if (gameTypes.Count() == 0)
                        throw new Exception("Could not find any game scripts!");
                    else if (gameTypes.Count() > 1)
                        throw new Exception("Found multiple game scripts!  In future, you will be able to choose.");

                    IGame game = (IGame)Activator.CreateInstance(gameTypes.First());
                    GameState initialState = game.InitialState;

                    using (RenderSystem renderSystem = (RenderSystem)Activator.CreateInstance(this.renderSystemType, resourceMgr))
                    {
                        using (InputManager inputMgr = new InputManager())
                        {
                            // Create initial state.
                            using (GameStateManager gameStateMgr = new GameStateManager(resourceMgr, renderSystem, inputMgr, scriptMgr, initialState, false))
                            {
                                initialState.InitialiseManagers(resourceMgr, gameStateMgr, renderSystem, inputMgr, scriptMgr);

                                using (Window w = new Window(this.windowTitle,
                                                             this.windowWidth,
                                                             this.windowHeight,
                                                             this.fullScreen,
                                                             this.useVSync,
                                                             renderSystem,
                                                             gameStateMgr,
                                                             inputMgr))
                                {
                                    // Run at same frequency for both updates and rendering.
                                    w.Run(updatesPerSecond, framesPerSecond);

                                    // Window controls the OpenGL context, so we must dispose of
                                    // rendersystem resources before the window is disposed of,
                                    // otherwise we are freeing memory twice.
                                    renderSystem.DisposeAllResources();
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
